﻿Imports System.Activities.Presentation.View
Imports System.Windows.Controls.Primitives
Imports System.Activities.Presentation.Model
Imports System.Activities.Expressions
Imports Microsoft.VisualBasic.Activities
Imports System.Activities

Public Class EditorInstance
    Implements IExpressionEditorInstance

#Region "変数 Variables"
    'Private editor As HilightTextBox = Nothing
    Private editor As TextBox = Nothing
    Private isPopup As IntellisSencePopUp = Nothing
    Private _startText As String = ""
#End Region

#Region "コンストラクタ New-Finalize"
    Public Sub New()
        'editor = New HilightTextBox With {
        '    .IntellisenceList = Me.IntellisenceList,
        '    .HighlightWords = Me.HighlightWords}
        editor = New TextBox
        AddHandler editor.KeyDown, AddressOf EditorKeyDown
        AddHandler editor.TextChanged, AddressOf EditorTextChanged
        AddHandler editor.PreviewKeyDown, AddressOf EditorKeyPress
        AddHandler editor.LostFocus, AddressOf EditorLostFocus
    End Sub

#End Region

#Region "イベント Event"
    Public Event TextChanged(sender As Object, e As System.EventArgs) Implements IExpressionEditorInstance.TextChanged
    Public Event Closing(sender As Object, e As System.EventArgs) Implements IExpressionEditorInstance.Closing
    Public Event GotAggregateFocus(sender As Object, e As System.EventArgs) Implements IExpressionEditorInstance.GotAggregateFocus
    Public Event LostAggregateFocus(sender As Object, e As System.EventArgs) Implements IExpressionEditorInstance.LostAggregateFocus
#End Region

#Region "プロパティ Property"

    Friend Property IntellisenceList As TreeNodes
    Friend Property HighlightWords As String
    Friend Property ExpressionType As Type
    Friend Property Guid As Guid

    Public Property AcceptsReturn As Boolean Implements System.Activities.Presentation.View.IExpressionEditorInstance.AcceptsReturn
        Get
            Return editor.AcceptsReturn
        End Get
        Set(value As Boolean)
            If editor.AcceptsReturn <> value Then editor.AcceptsReturn = value
        End Set
    End Property

    Public Property AcceptsTab As Boolean Implements System.Activities.Presentation.View.IExpressionEditorInstance.AcceptsTab
        Get
            Return editor.AcceptsTab
        End Get
        Set(value As Boolean)
            If editor.AcceptsTab <> value Then editor.AcceptsTab = value
        End Set
    End Property

    Public ReadOnly Property HasAggregateFocus As Boolean Implements System.Activities.Presentation.View.IExpressionEditorInstance.HasAggregateFocus
        Get
            Return True
        End Get
    End Property

    Public Property HorizontalScrollBarVisibility As System.Windows.Controls.ScrollBarVisibility Implements System.Activities.Presentation.View.IExpressionEditorInstance.HorizontalScrollBarVisibility
        Get
            Return editor.HorizontalScrollBarVisibility
        End Get
        Set(value As System.Windows.Controls.ScrollBarVisibility)
            editor.HorizontalScrollBarVisibility = value
        End Set
    End Property

    Public ReadOnly Property HostControl As System.Windows.Controls.Control Implements System.Activities.Presentation.View.IExpressionEditorInstance.HostControl
        Get
            Return editor
        End Get
    End Property

    Public Property MaxLines As Integer Implements System.Activities.Presentation.View.IExpressionEditorInstance.MaxLines
        Get
            Return editor.MaxLines
        End Get
        Set(value As Integer)
            If editor.MaxLines <> value Then editor.MaxLines = value
        End Set
    End Property

    Public Property MinLines As Integer Implements System.Activities.Presentation.View.IExpressionEditorInstance.MinLines
        Get
            Return editor.MinLines
        End Get
        Set(value As Integer)
            If editor.MinLines <> value Then editor.MinLines = value
        End Set
    End Property

    Public Property Text As String Implements System.Activities.Presentation.View.IExpressionEditorInstance.Text
        Get
            Return editor.Text
        End Get
        Set(value As String)
            editor.Text = value
        End Set
    End Property

    Public Property VerticalScrollBarVisibility As System.Windows.Controls.ScrollBarVisibility Implements System.Activities.Presentation.View.IExpressionEditorInstance.VerticalScrollBarVisibility
        Get
            Return editor.VerticalScrollBarVisibility
        End Get
        Set(value As System.Windows.Controls.ScrollBarVisibility)
            editor.VerticalScrollBarVisibility = value
        End Set
    End Property
#End Region

#Region "メソッド Method"
    Public Function CanCompleteWord() As Boolean Implements System.Activities.Presentation.View.IExpressionEditorInstance.CanCompleteWord
        Return True
    End Function

    Public Function CanCopy() As Boolean Implements System.Activities.Presentation.View.IExpressionEditorInstance.CanCopy
        Return True
    End Function

    Public Function CanCut() As Boolean Implements System.Activities.Presentation.View.IExpressionEditorInstance.CanCut
        Return True
    End Function

    Public Function CanDecreaseFilterLevel() As Boolean Implements System.Activities.Presentation.View.IExpressionEditorInstance.CanDecreaseFilterLevel
        Return False
    End Function

    Public Function CanGlobalIntellisense() As Boolean Implements System.Activities.Presentation.View.IExpressionEditorInstance.CanGlobalIntellisense
        Return False
    End Function

    Public Function CanIncreaseFilterLevel() As Boolean Implements System.Activities.Presentation.View.IExpressionEditorInstance.CanIncreaseFilterLevel
        Return False
    End Function

    Public Function CanParameterInfo() As Boolean Implements System.Activities.Presentation.View.IExpressionEditorInstance.CanParameterInfo
        Return False
    End Function

    Public Function CanPaste() As Boolean Implements System.Activities.Presentation.View.IExpressionEditorInstance.CanPaste
        Return True
    End Function

    Public Function CanQuickInfo() As Boolean Implements System.Activities.Presentation.View.IExpressionEditorInstance.CanQuickInfo
        Return False
    End Function

    Public Function CanRedo() As Boolean Implements System.Activities.Presentation.View.IExpressionEditorInstance.CanRedo
        Return editor.CanRedo
    End Function

    Public Function CanUndo() As Boolean Implements System.Activities.Presentation.View.IExpressionEditorInstance.CanUndo
        Return editor.CanUndo
    End Function

    Public Sub ClearSelection() Implements System.Activities.Presentation.View.IExpressionEditorInstance.ClearSelection
        Return
    End Sub

    Public Sub Close() Implements System.Activities.Presentation.View.IExpressionEditorInstance.Close
        Return
    End Sub

    Public Function CompleteWord() As Boolean Implements System.Activities.Presentation.View.IExpressionEditorInstance.CompleteWord
        Return True
    End Function

    Public Function Copy() As Boolean Implements System.Activities.Presentation.View.IExpressionEditorInstance.Copy
        Return True
    End Function

    Public Function Cut() As Boolean Implements System.Activities.Presentation.View.IExpressionEditorInstance.Cut
        Return True
    End Function

    Public Function DecreaseFilterLevel() As Boolean Implements System.Activities.Presentation.View.IExpressionEditorInstance.DecreaseFilterLevel
        Return False
    End Function

    Public Sub Focus() Implements System.Activities.Presentation.View.IExpressionEditorInstance.Focus
        editor.Focus()
    End Sub

    Public Function GetCommittedText() As String Implements System.Activities.Presentation.View.IExpressionEditorInstance.GetCommittedText
        Return editor.Text
    End Function

    Public Function GlobalIntellisense() As Boolean Implements System.Activities.Presentation.View.IExpressionEditorInstance.GlobalIntellisense
        Return False
    End Function

    Public Function IncreaseFilterLevel() As Boolean Implements System.Activities.Presentation.View.IExpressionEditorInstance.IncreaseFilterLevel
        Return False
    End Function

    Public Function ParameterInfo() As Boolean Implements System.Activities.Presentation.View.IExpressionEditorInstance.ParameterInfo
        Return False
    End Function

    Public Function Paste() As Boolean Implements System.Activities.Presentation.View.IExpressionEditorInstance.Paste
        Return True
    End Function

    Public Function QuickInfo() As Boolean Implements System.Activities.Presentation.View.IExpressionEditorInstance.QuickInfo
        Return False
    End Function

    Public Function Redo() As Boolean Implements System.Activities.Presentation.View.IExpressionEditorInstance.Redo
        Return True
    End Function

    Public Function Undo() As Boolean Implements System.Activities.Presentation.View.IExpressionEditorInstance.Undo
        Return True
    End Function
#End Region

#Region "エディタ・ポップアップイベント Editor Popup-Event"

    Private Sub EditorTextChanged(ByVal sender As Object, ByVal e As EventArgs)
        Me.Text = editor.Text

        If (isPopup Is Nothing) Then Return

        Dim inpText = Me.GetInputingText
        Dim targetNode = Me.SearchNodes(IntellisenceList, inpText)
        If targetNode Is Nothing Then targetNode = IntellisenceList

        Dim targetText = inpText.ToLower
        If targetText.EndsWith(".") Then
            targetText = targetText.Substring(0, targetText.Length - 1)
        End If
        Dim searchList = From x In targetNode.Nodes
                                Where x.GetFullPath.ToLower.Contains(targetText)
                                Select x

        '変数や引数の際はそれらの Type に対してのインテリセンスを有効にする
        If IsVarOrArg(targetText) Then
            Dim itemNode = Me.SearchNodes(IntellisenceList, targetText)
            If itemNode IsNot Nothing Then
                Dim itemTypeNode = Me.SearchNodes(IntellisenceList, itemNode.SystemType.FullName)
                If (itemTypeNode IsNot Nothing) AndAlso
                    (itemTypeNode.Nodes.Count > 0) Then searchList = searchList.Union(itemTypeNode.Nodes)
            End If
        End If

        If (searchList Is Nothing) OrElse (searchList.Count <= 0) Then
            Me.UnInitilaizePopup()
            Return
        End If

        isPopup.DataContext = Nothing
        isPopup.DataContext = searchList

    End Sub

    Private Function IsVarOrArg(ByVal inputText As String) As Boolean
        Dim result = False

        Dim searchList = From x In IntellisenceList.Nodes
                                Where x.Name.ToLower = inputText.ToLower AndAlso
                                          x.SystemType IsNot Nothing
                                Select x

        If (searchList IsNot Nothing) AndAlso (searchList.Count > 0) Then
            result = True
        End If

        Return result
    End Function

    Private Function GetInputingText() As String
        Dim inpText = editor.Text
        If inpText.ToLower.StartsWith("new") Then
            'New は除去
            inpText = inpText.Substring(3).Trim
        End If
        Return inpText
    End Function

    Private Sub EditorKeyDown(ByVal sender As Object, ByVal e As KeyEventArgs)

        If (Not Me.AcceptsReturn AndAlso e.Key = Key.Enter AndAlso Keyboard.Modifiers = ModifierKeys.None) OrElse
            (Not Me.AcceptsTab AndAlso e.Key = Key.Tab AndAlso Keyboard.Modifiers = ModifierKeys.None) Then
            e.Handled = True

            Dim request As New TraversalRequest(FocusNavigationDirection.Next)
            Dim uIElement = Me.GetFocusedElement()
            If uIElement IsNot Nothing Then uIElement.MoveFocus(request)
            Return
        End If

    End Sub

    Private Sub EditorKeyPress(ByVal sender As Object, ByVal e As KeyEventArgs)

        If (e.Key = Key.Up) Then
            If (isPopup Is Nothing) OrElse (Not isPopup.IsOpen) Then Return
            isPopup.SelectedIndex -= 1
            e.Handled = True
        End If

        If (e.Key = Key.Down) Then
            If (isPopup Is Nothing) OrElse (Not isPopup.IsOpen) Then Return
            isPopup.SelectedIndex += 1
            e.Handled = True
        End If

        If e.Key = Key.Escape Then
            If isPopup IsNot Nothing Then isPopup.IsOpen = False
            _startText = ""
            e.Handled = True
            Return
        End If

        If (e.Key = Key.Decimal OrElse e.Key = Key.OemPeriod OrElse
            (e.Key = Key.Space AndAlso Keyboard.Modifiers = ModifierKeys.Control)) Then

            If (isPopup IsNot Nothing) AndAlso (isPopup.IsOpen) Then Return
            'インテリセンスの開始
            Dim searchWord = editor.Text
            If (e.Key = Key.Decimal OrElse e.Key = Key.OemPeriod) Then
                searchWord += "."
            End If
            _startText = GetCommitedWord(searchWord)

            If searchWord.EndsWith(".") Then searchWord = searchWord.Substring(0, searchWord.Length - 1)
            If searchWord.ToLower.StartsWith("new ") Then searchWord = searchWord.Substring(4)
            Dim targetNodes = Me.SearchNodes(IntellisenceList, searchWord)
            If targetNodes Is Nothing Then targetNodes = IntellisenceList
            Dim isSource = targetNodes.Nodes.ToList
            Me.InitializePopup(isSource)
            isPopup.IsOpen = True
            If (e.Key = Key.Space AndAlso Keyboard.Modifiers = ModifierKeys.Control) Then e.Handled = True
            Return
        End If

        If (e.Key = Key.Home) Then
            If (isPopup Is Nothing) OrElse (Not isPopup.IsOpen) Then Return
            isPopup.SelectedIndex = 0
            e.Handled = True
        End If

        If (e.Key = Key.End) Then
            If (isPopup Is Nothing) OrElse (Not isPopup.IsOpen) Then Return
            isPopup.SelectedIndex = isPopup.ItemsCount - 1
            e.Handled = True
        End If

        If (e.Handled = False) AndAlso
            ((e.Key = Key.Enter) OrElse (e.Key = Key.Space) OrElse (e.Key = Key.Tab)) Then
            If (isPopup IsNot Nothing) AndAlso (isPopup.IsOpen) Then
                '確定
                Dim isSource = isPopup.SelectedItem
                If isSource Is Nothing Then Return
                Me.CommitIntellisenseItem(isSource)
                e.Handled = True
            End If
        End If
    End Sub

    Private Sub EditorLostFocus(ByVal sender As Object, ByVal e As EventArgs)
        Dim popupItem = TryCast(Me.GetFocusedElement(), ListBoxItem)
        If popupItem Is Nothing Then
            If (isPopup IsNot Nothing) AndAlso (isPopup.IsOpen) Then
                Me.UnInitilaizePopup()
            End If
            RaiseEvent LostAggregateFocus(sender, e)
        End If

    End Sub

    Private Sub ListKeyDown(ByVal sender As Object, ByVal e As KeyEventArgs)
        If e.Key = Key.Escape Then
            editor.Focus()
            Me.UnInitilaizePopup()
            Return
        End If

        If (e.Key = Key.Enter) OrElse (e.Key = Key.Space) OrElse (e.Key = Key.Tab) Then
            '確定
            editor.Focus()

            Dim isSource = isPopup.SelectedItem
            If isSource Is Nothing Then Return
            Dim isText = isSource.GetFullPath
            editor.Text = _startText + isText
            editor.SelectionStart = editor.Text.Length
            Me.UnInitilaizePopup()
        End If

    End Sub

    Private Sub ListItemDoubleClick(ByVal sender As Object, ByVal e As MouseButtonEventArgs)
        Dim item = TryCast(sender, ListBoxItem)
        If item Is Nothing Then Return
        Dim nodes = TryCast(item.DataContext, TreeNodes)
        If nodes Is Nothing Then Return
        editor.Focus()
        Me.CommitIntellisenseItem(nodes)
    End Sub

    Private Sub CommitIntellisenseItem(ByVal selectedNodes As TreeNodes)
        Dim inputText = _startText
        inputText += selectedNodes.Name

        editor.Text = inputText
        editor.SelectionStart = editor.Text.Length
        editor.UpdateLayout()
        Me.UnInitilaizePopup()
    End Sub

    Private Function GetCommitedWord(ByVal inputedText As String) As String
        Dim spacePos = inputedText.LastIndexOf(" ")
        Dim parenthesisPos = inputedText.LastIndexOf("(")
        Dim dotPos = inputedText.LastIndexOf(".")
        If spacePos = -1 AndAlso parenthesisPos = -1 AndAlso dotPos = -1 Then Return inputedText

        If (spacePos > parenthesisPos) AndAlso (dotPos = -1) Then
            Return inputedText.Substring(0, spacePos + 1)
        ElseIf (dotPos = -1) Then
            Return inputedText.Substring(0, parenthesisPos + 1)
        Else
            Return inputedText.Substring(0, dotPos + 1)
        End If
    End Function

    Private Function GetFocusedElement() As UIElement
        Return TryCast(Keyboard.FocusedElement, UIElement)
    End Function

    Private Sub InitializePopup(ByVal isSource As List(Of TreeNodes))
        If (isPopup IsNot Nothing) AndAlso (isPopup.IsOpen) Then Me.UnInitilaizePopup()

        isPopup = New IntellisSencePopUp With {
                            .DataContext = isSource,
                            .PlacementTarget = editor,
                            .Placement = PlacementMode.Bottom
                            }

        AddHandler isPopup.ListBoxKeyDown, AddressOf ListKeyDown
        AddHandler isPopup.ListBoxItemDoubleClick, AddressOf ListItemDoubleClick

    End Sub

    Private Sub UnInitilaizePopup()
        If isPopup Is Nothing Then Return

        RemoveHandler isPopup.ListBoxKeyDown, AddressOf ListKeyDown
        RemoveHandler isPopup.ListBoxItemDoubleClick, AddressOf ListItemDoubleClick

        If isPopup.IsOpen Then isPopup.IsOpen = False
        _startText = ""
        isPopup = Nothing

    End Sub

    Private Function SearchNodes(ByVal targetNodes As TreeNodes, ByVal namePath As String) As TreeNodes
        Dim targetPath = namePath.Split(".")
        Dim validPath As Boolean = False
        Dim existsNodes As TreeNodes = Nothing
        For Each childNode In targetNodes.Nodes
            If childNode.Name.ToLower <> targetPath(0).ToLower Then Continue For
            validPath = True
            existsNodes = childNode
            Exit For
        Next
        If Not validPath Then Return targetNodes

        Dim nextPath = namePath.Substring(targetPath(0).Length, namePath.Length - targetPath(0).Length)
        If nextPath.StartsWith(".") Then nextPath = nextPath.Substring(1, nextPath.Length - 1)
        If nextPath.Trim = "" Then Return existsNodes
        Return Me.SearchNodes(existsNodes, nextPath)
    End Function

#End Region


End Class
